﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
#if !WINDOWS_PHONE


#endif

namespace DARE
{
    public class CHeightmapModel : CBasicModel
    {

    #region fields

        private CHeightMapInfo m_heightMapInfo = null;
        private Vector2 m_texCoordOffset;

    #endregion

    #region properties

        public CHeightMapInfo HeightMapInfo
        {
            get { return m_heightMapInfo; }
        }

    #endregion

    #region ctor

        public CHeightmapModel(CHeightMapInfo hInfo) : 
            base(1.0f, 1.0f, 1.0f)
        {
            Initialize(hInfo, Vector2.Zero);
        }

        public CHeightmapModel(string name, CHeightMapInfo hInfo) : 
            base (name, 1.0f, 1.0f, 1.0f)
        {
            Initialize(hInfo, Vector2.Zero);
        }

        internal CHeightmapModel(CHeightMapInfo hInfo, Vector2 TexCoordOffset) : 
            base(1.0f, 1.0f, 1.0f)
        {
            Initialize(hInfo, TexCoordOffset);
        }

        private void Initialize(CHeightMapInfo hInfo, Vector2 TexCoordOffset)
        {
            m_texCoordOffset = TexCoordOffset;
            m_primitiveType = PrimitiveType.TriangleList;
            //m_cullMode = CullMode.None;
            CullMode = CullMode.None;
            m_heightMapInfo = hInfo;
            SetUpVertices();
            SetUpIndices();
            SetUpNormals();
            CreateVertexBuffer();
            CreateIndexBuffer();
        }

        public override AModel Clone()
        {
            CHeightmapModel clone = new CHeightmapModel(m_name, m_heightMapInfo);

            base.CopyPropertiesTo(clone);
            return clone;
        }

    #endregion

    #region setup

        protected void SetUpVertices()
        {
#if WINDOWS_PHONE
            m_vertices = new VertexPositionNormalTexture[m_heightMapInfo.Width * m_heightMapInfo.Height];
#else
            m_vertices = new VertexPositionNormalTextureTangentBinormal[m_heightMapInfo.Width * m_heightMapInfo.Height];
#endif
            for (int x = 0; x < m_heightMapInfo.Width; ++x)
                for (int y = 0; y < m_heightMapInfo.Height; ++y)
                {
                    m_vertices[x + y * m_heightMapInfo.Width].Position = new Vector3(
                        ((x - (m_heightMapInfo.Width) / 2) + 0.5f) * m_heightMapInfo.TerrainScale,
                        m_heightMapInfo.Heights[x, y],
                        ((y - (m_heightMapInfo.Width) / 2) + 0.5f) * m_heightMapInfo.TerrainScale);
                    m_vertices[x + y * m_heightMapInfo.Width].TextureCoordinate = new Vector2(
                        (float)x / m_heightMapInfo.Width, 
                        (float)y / m_heightMapInfo.Height);
                }
        }

        protected void SetUpIndices()
        {
             m_indices = new short[(m_heightMapInfo.Width - 1) * (m_heightMapInfo.Height - 1) * 6];
             int counter = 0;

             for (int y = 0; y < m_heightMapInfo.Height - 1; ++y)
                 for (int x = 0; x < m_heightMapInfo.Width - 1; ++x)
                 {
                     int lowerLeft = x + y * m_heightMapInfo.Width;
                     int lowerRight = (x + 1) + y * m_heightMapInfo.Width;
                     int topLeft = x + (y + 1) * m_heightMapInfo.Width;
                     int topRight = (x + 1) + (y + 1) * m_heightMapInfo.Width;

                     m_indices[counter++] = (short)topLeft;
                     m_indices[counter++] = (short)lowerLeft;
                     m_indices[counter++] = (short)lowerRight;
         
                     m_indices[counter++] = (short)topRight;
                     m_indices[counter++] = (short)topLeft;
                     m_indices[counter++] = (short)lowerRight;
                 }
        }

        protected void SetUpNormals()
        {
             for (int i = 0; i < m_vertices.Length; i++)
                 m_vertices[i].Normal = Vector3.Zero;
             for (int i = 0; i < m_indices.Length / 3; i++)
             {
                 int index1 = m_indices[i * 3];
                 int index2 = m_indices[i * 3 + 1];
                 int index3 = m_indices[i * 3 + 2];
 
                 Vector3 side1 = m_vertices[index1].Position - m_vertices[index3].Position;
                 Vector3 side2 = m_vertices[index1].Position - m_vertices[index2].Position;
                 Vector3 normal = Vector3.Cross(side1, side2);
 
                 m_vertices[index1].Normal += normal;
                 m_vertices[index2].Normal += normal;
                 m_vertices[index3].Normal += normal;
             }
             for (int i = 0; i < m_vertices.Length; i++)
                 m_vertices[i].Normal.Normalize();
        }

        // old
        protected void SetupVertices()
        {
            int index = 0;
#if WINDOWS_PHONE
            m_vertices = new VertexPositionNormalTexture[m_heightMapInfo.Width * m_heightMapInfo.Height * 6];
#else
            m_vertices = new VertexPositionNormalTextureTangentBinormal[m_heightMapInfo.Width * m_heightMapInfo.Height * 6];
#endif
            // points definition and normal
            Vector3 bottomLeft = Vector3.Zero;
            Vector3 bottomRight = Vector3.Zero;
            Vector3 topRight = Vector3.Zero;
            Vector3 topLeft = Vector3.Zero;
            Vector3 normal = Vector3.Zero;

            // set vertices
            for (int z = 0; z < m_heightMapInfo.Height - 1; ++z)
                for (int x = 0; x < m_heightMapInfo.Width - 1; ++x)
                {
                    // calculate the points and the normal
                    bottomLeft = new Vector3(
                        (x - m_heightMapInfo.Width / 2) * m_heightMapInfo.TerrainScale, 
                        m_heightMapInfo.Heights[x, z], 
                        (z - m_heightMapInfo.Height / 2) * m_heightMapInfo.TerrainScale);
                    bottomRight = new Vector3(
                        (x + 1 - m_heightMapInfo.Width / 2) * m_heightMapInfo.TerrainScale, 
                        m_heightMapInfo.Heights[x + 1, z], 
                        (z - m_heightMapInfo.Height / 2) * m_heightMapInfo.TerrainScale);
                    topLeft = new Vector3(
                        (x - m_heightMapInfo.Width / 2) * m_heightMapInfo.TerrainScale, 
                        m_heightMapInfo.Heights[x, z + 1], 
                        (z + 1 - m_heightMapInfo.Height / 2) * m_heightMapInfo.TerrainScale);
                    topRight = new Vector3(
                        (x + 1 - m_heightMapInfo.Width / 2) * m_heightMapInfo.TerrainScale, 
                        m_heightMapInfo.Heights[x + 1, z + 1], 
                        (z + 1 - m_heightMapInfo.Height / 2) * m_heightMapInfo.TerrainScale);
                    normal = Vector3.Cross(topLeft - bottomLeft, bottomRight - bottomLeft);
                    normal.Normalize();
#if WINDOWS_PHONE
                    m_vertices[index++] = new VertexPositionNormalTexture(bottomLeft, normal, new Vector2(x / m_heightMapInfo.Width, z / m_heightMapInfo.Height));
                    m_vertices[index++] = new VertexPositionNormalTexture(bottomRight, normal, new Vector2((x + 1) / m_heightMapInfo.Width, z / m_heightMapInfo.Height));
                    m_vertices[index++] = new VertexPositionNormalTexture(topRight, normal, new Vector2((x + 1) / m_heightMapInfo.Width, (z + 1) / m_heightMapInfo.Height));

                    m_vertices[index++] = new VertexPositionNormalTexture(bottomLeft, normal, new Vector2(x / m_heightMapInfo.Width, z / m_heightMapInfo.Height));
                    m_vertices[index++] = new VertexPositionNormalTexture(topRight, normal, new Vector2((x + 1) / m_heightMapInfo.Width, (z + 1) / m_heightMapInfo.Height));
                    m_vertices[index++] = new VertexPositionNormalTexture(topLeft, normal, new Vector2(x / m_heightMapInfo.Width, (z + 1) / m_heightMapInfo.Height));
#else
                    m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomLeft, normal, new Vector2(x / m_heightMapInfo.Width, z / m_heightMapInfo.Height));
                    m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomRight, normal, new Vector2((x + 1) / m_heightMapInfo.Width, z / m_heightMapInfo.Height));
                    m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topRight, normal, new Vector2((x + 1) / m_heightMapInfo.Width, (z + 1) / m_heightMapInfo.Height));

                    m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(bottomLeft, normal, new Vector2(x / m_heightMapInfo.Width, z / m_heightMapInfo.Height));
                    m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topRight, normal, new Vector2((x + 1) / m_heightMapInfo.Width, (z + 1) / m_heightMapInfo.Height));
                    m_vertices[index++] = new VertexPositionNormalTextureTangentBinormal(topLeft, normal, new Vector2(x / m_heightMapInfo.Width, (z + 1) / m_heightMapInfo.Height));
#endif
                }

            // create the vertex buffer
            m_vertexBuffer = new VertexBuffer(CDare.Instance.GraphicsDevice, typeof(VertexPositionNormalTextureTangentBinormal), m_vertices.Length, BufferUsage.WriteOnly);
            m_vertexBuffer.SetData(m_vertices);
        }

    #endregion

    }
}
